/*
 * Copyright (C) 2010 Fores Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package jp.fores.midori.server.plugin.impl;

import jp.fores.midori.core.dto.RPCInputDto;
import jp.fores.midori.core.dto.RPCOutputDto;
import jp.fores.midori.server.WebContext;
import jp.fores.midori.server.exception.MidoriServerException;
import jp.fores.midori.server.exception.RPCIllegalRequestException;
import jp.fores.midori.server.plugin.AbstractRPCPlugin;
import jp.fores.midori.xmlrpc.MethodNameAndArgsDto;
import jp.fores.midori.xmlrpc.XmlRpcUtil;

import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


/**
 * Apache XML-RPC形式のリクエスト・レスポンスの処理を行うプラグインクラス。<br>
 */
public class ApacheXmlRPCPlugin extends AbstractRPCPlugin {
	//==========================================================
	//定数

	/**
	 * ログ出力用
	 */
	private static final Log log = LogFactory.getLog(ApacheXmlRPCPlugin.class);

	/**
	 * Content-Type
	 */
	public static final String CONTENT_TYPE = "text/xml";


	//==========================================================
	//メソッド

	//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
	//抽象メソッドの実装

	/**
	 * {@inheritDoc}
	 */
	@Override
	public RPCInputDto requestToInputDto(WebContext context)
			throws RPCIllegalRequestException, Exception {
		//デバッグログが有効な場合
		if (log.isDebugEnabled()) {
			//リクエストの入力ストリームの内容を「UTF-8」の文字列として取得
			String xmlStr = IOUtils.toString(context.request.getInputStream(), "UTF-8");

			//デバッグログ出力
			log.debug("XML-RPCの入力用のXMLの文字列\n" + xmlStr);

			//XML-RPCの入力用のXMLの文字列をRPCの入力データを格納するためのDTOに変換して返す
			return convertToRPCInputDto(XmlRpcUtil.inputXMLToDto(xmlStr));
		}
		//それ以外の場合
		else {
			//リクエストの入力ストリームから読み込んだXML-RPCの入力用のXMLの文字列をRPCの入力データを格納するためのDTOに変換して返す
			//(一度入力ストリームを文字列に変換してから処理を行うよりも、直接入力ストリームから処理を行う方が効率が良いので、
			// デバッグ時以外はこちらの方法で処理を行う)
			return convertToRPCInputDto(XmlRpcUtil.inputXMLToDto(context.request.getInputStream()));
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void outputDtoToResponse(RPCOutputDto outputDto, WebContext context)
			throws Exception {
		//レスポンスのContent-Typeに定数の値を設定する
		context.response.setContentType(CONTENT_TYPE);

		//出力データ用のDTOをXML-RPCの出力用のXMLの文字列
		final String xmlStr;

		//出力データの戻り値のコードが「OK」の場合
		if (outputDto.returnCode == RPCOutputDto.RETURN_CODE_OK) {
			//Apache XML-RPCのレスポンス形式のXML文字列を作成する
			xmlStr = XmlRpcUtil.createResponseApacheXMLString(outputDto.data);
		}
		//それ以外の場合
		else {
			//DTOのデータ部分を元の型(リモートプロシージャーコール用のサーブレットで発生する例外)にキャスト
			MidoriServerException rpcServletException = (MidoriServerException) outputDto.data;

			//Apache XML-RPCのエラーレスポンス形式のXML文字列を作成する
			//(エラーコードとエラー文字列には例外から取得した値を指定する)
			xmlStr = XmlRpcUtil.createErrorResponseApacheXMLString(rpcServletException.getErrorCode(), rpcServletException.getMessage());
		}

		//デバッグログが有効な場合
		if (log.isDebugEnabled()) {
			//デバッグログ出力
			log.debug("XML-RPCの出力用のXMLの文字列\n" + xmlStr);
		}

		//出力用のXMLの文字列をバイト配列に変換
		//(文字コードはUTF-8固定)
		byte[] outputByteArray = xmlStr.getBytes("UTF-8");

		//レスポンスのContent-Lengthに出力データ用のバイト配列のサイズを設定する
		context.response.setContentLength(outputByteArray.length);

		//出力データ用のバイト配列をレスポンスの出力ストリームに書き込む
		IOUtils.write(outputByteArray, context.response.getOutputStream());
	}


	//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
	//内部処理用

	/**
	 * メソッド名と引数の情報を格納するためのDTOを、RPCの入力データを格納するためのDTOに変換します。<br>
	 *
	 * @param dto メソッド名と引数の情報を格納するためのDTO
	 * @return RPCの入力データを格納するためのDTO
	 */
	private RPCInputDto convertToRPCInputDto(MethodNameAndArgsDto dto) {
		//RPCの入力データを格納するためのDTOのインスタンスを生成
		RPCInputDto resultDto = new RPCInputDto();

		//引数のDTOの各フィールドの値をそのままコピーする
		resultDto.methodName = dto.methodName;
		resultDto.args = dto.args;

		//作成したDTOを返す
		return resultDto;
	}
}
